﻿using System;
using System.Collections.Generic;
using Gp.Input;
using UnityEngine;
using UnityEngine.EventSystems;


namespace Gp.Scripts.Core {
    public enum MouseMode {
        /// <summary>
        /// 锁定鼠标，在普通游戏模式下
        /// </summary>
        GamePlay,

        /// <summary>
        /// UI模式，将鼠标限制在game Window 中, 可活动
        /// </summary>
        UI,
    }


    public static class MouseModeExtension {
        public static void ModeMotion(this MouseMode mode) {
            switch (mode) {
                case MouseMode.GamePlay:
                    Cursor.lockState = CursorLockMode.Locked;
                    break;
                case MouseMode.UI:
                    Cursor.visible = true;
                    Cursor.lockState = CursorLockMode.Confined;
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(mode), mode, null);
            }
        }
    }


    public abstract class ObjectMouseHandleAgent<T> : ObjectMouseHandleAgent where T : Component {
        public virtual string TagFilter => null;

        
        protected override void OnMouseEnterTargetObject(GameObject gameObject) {
            if (gameObject == null || !TagJudge(gameObject)) return;
            var arg = gameObject.GetComponent<T>();
            if (arg) OnMouseEnter(arg);
        }

        protected override void OnMouseExitTargetObject(GameObject gameObject) {
            if (gameObject == null || !TagJudge(gameObject)) return;
            var arg = gameObject.GetComponent<T>();
            if (arg) OnMouseExit(arg);
        }

        protected override void OnMouseClickTargetObject(GameObject gameObject) {
            if (gameObject == null || !TagJudge(gameObject)) return;
            var arg = gameObject.GetComponent<T>();
            if (arg) OnMouseClick(arg);
        }

        private bool TagJudge(GameObject obj) {
            return TagFilter == null || obj.CompareTag(TagFilter);
        }
        

        protected virtual void OnMouseClick(T gameObject) { }


        protected virtual void OnMouseExit(T gameObject) { }

        protected virtual void OnMouseEnter(T target) { }
    }


    public abstract class MouseHandleAgent {
        public virtual int Layer => -1;

        
        
        public virtual void OnUpdate(RaycastHit hit, bool isHit) { }
        
        
        
        public virtual void OnRegister() { }

        public virtual void OnUnRegister() { }

        public virtual void OnMouseDown() { }
    }


    /// <summary>
    /// 针对某类对象的鼠标管理Agent。处理鼠标移入、移出、点击某对象功能。
    /// </summary>
    public abstract class ObjectMouseHandleAgent : MouseHandleAgent{
        /// <summary>
        /// 当前光标指向的对象
        /// </summary>
        protected GameObject CurMouseOverObject { get; private set; }


        public override void OnUpdate(RaycastHit hit, bool isHit) {
            if (!isHit) {
                OnMouseExitTargetObject(CurMouseOverObject);
                CurMouseOverObject = null;
                return;
            }

            var hitObj = hit.collider.gameObject;
            if (hitObj == CurMouseOverObject) return;

            OnMouseExitTargetObject(CurMouseOverObject);
            CurMouseOverObject = hitObj;
            OnMouseEnterTargetObject(CurMouseOverObject);
        }


        public override void OnMouseDown() {
            if (CurMouseOverObject) OnMouseClickTargetObject(CurMouseOverObject);
        }


        protected virtual void OnMouseEnterTargetObject(GameObject gameObject) { }

        protected virtual void OnMouseExitTargetObject(GameObject gameObject) {
            if (gameObject == null) return;
        }

        protected virtual void OnMouseClickTargetObject(GameObject gameObject) { }


    }

    [AutoLoad]
    public class CursorManager : BaseGameManager {
        public Texture2D cursorTexture;
        private MouseMode _mode;
        private readonly List<MouseHandleAgent> _handlers = new();
        private readonly List<MouseHandleAgent> _removed = new();

        protected override void OnInit() {
            DefaultLayer = Physics.DefaultRaycastLayers;
        }

        public override void OnUpdate() {
            foreach (var a in _removed) {
                _handlers.Remove(a);
            }

            _removed.Clear();
            if (_handlers.Count == 0) return;
            var uiCast = IsPointerOverUIObject();
            foreach (var agent in _handlers) {
                var layer = agent.Layer;
                RaycastHit hit = default;
                var isHit = !uiCast && Raycast(out hit, layer);
                agent.OnUpdate(hit, isHit);
            }

            if (!IsMouseButtonDown()) {
                return;
            }


            foreach (var agent in _handlers) {
                agent.OnMouseDown();
            }
        }


        public void SetMode(MouseMode mode) { }


        // public void ShowHand() {
        //     Cursor.SetCursor(cursorTexture, Vector2.zero, CursorMode.Auto);
        // }
        //
        // public void Recover() {
        //     Cursor.SetCursor(null, Vector2.zero, CursorMode.Auto);
        // }
        //
        //
        /// <summary>
        /// 绑定某类gameObject的
        /// </summary>
        /// <param name="agent"></param>
        public void BindObjects(MouseHandleAgent agent) {
            agent.OnRegister();
            _handlers.Add(agent);
        }
        
        
        public void UnRegisterHandler(MouseHandleAgent agent) {
            agent.OnUnRegister();
            _removed.Add(agent);
        }


        /// <summary>
        /// 检测当前帧中鼠标是否按下<br/>
        /// 0 => 左键<br/>
        /// 1 => 右键<br/>
        /// 2 => 中键<br/>
        /// </summary>
        /// <param name="id"> 鼠标按键id </param>
        /// <returns></returns>
        public bool IsMouseButtonDown(int id = 0) {
            // 如有需要，可检测输入层级
            var mouse = InputManager.CurrentMouse();
            var button = id switch {
                0 => mouse.leftButton,
                1 => mouse.rightButton,
                2 => mouse.middleButton,
                _ => throw new ArgumentOutOfRangeException(nameof(id), id, null)
            };
            return button.wasPressedThisFrame;
        }


        public int DefaultLayer { get; private set; }

        public bool Raycast(out RaycastHit hit, int layer = -1, float maxDistance = Mathf.Infinity) {
            hit = default;
            if (!Ray(out var ray) || IsPointerOverUIObject()) return false;

            layer = layer < 0 ? DefaultLayer : layer;
            return Physics.Raycast(ray, out hit, maxDistance, layer);
        }

        public T Raycast<T>(int layer = -1, float maxDistance = Mathf.Infinity) where T : Component {
            return !Raycast(out var hit, layer, maxDistance) ? null : hit.collider.GetComponent<T>();
        }

        public bool Ray(out Ray ray) {
            ray = default;
            if (Camera.main == null) return false;
            ray = Camera.main.ScreenPointToRay(Global.Get<InputManager>().MousePosition());
            return true;
        }


        public Vector3 MouseUIPosition(RectTransform trans) {
            return Global.Get<InputManager>().MouseUIPosition(trans);
        }


        public static bool IsPointerOverUIObject() {
            return EventSystem.current.IsPointerOverGameObject();
        }
    }
}